use std::time::SystemTime;

pub struct SendError(String);

pub fn send(value: String) -> Result<(), SendError> {
    println!("using {value} in a meaningful way");
    // Simulate non-deterministic fallible action.

    let period = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap();

    if period.subsec_nanos() % 2 == 1 {
        Ok(())
    } else {
        Err(SendError(value))
    }
}

fn main() {
    let mut value = "imagine this is very long string".to_string();

    let success = 's: {
        // Try to send 'value' two times.
        for _ in 0..2 {
            value = match send(value) {
                Ok(()) => break 's true,
                Err(SendError(value)) => value,
            }
        }

        false
    };

    // We have 3 possible outcomes:
    //
    // 1.
    // using imagine this is very long string in a meaningful way
    // success: true
    //
    // 2.
    // using imagine this is very long string in a meaningful way
    // using imagine this is very long string in a meaningful way
    // success: false
    //
    // 3.
    // using imagine this is very long string in a meaningful way
    // using imagine this is very long string in a meaningful way
    // success: true
    println!("success: {success}");

    // In case of error you may want to try some alternative way or to retry
    // action in case of non-deterministic function. But if the argument is
    // always consumed, you are forced to clone it on every call, which is not
    // very efficient.
    //
    // The standard library uses this approach in e.g. String::from_utf8 method.
    // When given a vector that doesn’t contain valid UTF-8, a FromUtf8Error is
    // returned. You can get original vector back using FromUtf8Error::into_bytes
    // method.
    {
        // Some bytes, in a vector.
        let sparkle_heart = vec![240, 159, 146, 150];
        // We know these bytes are valid, so just use `unwrap()`.
        let sparkle_heart = String::from_utf8(sparkle_heart).unwrap();
        assert_eq!("💖", sparkle_heart);

        // Some invalid bytes, in a vector.
        let sparkle_heart = vec![0, 159, 146, 150];
        let sparkle_heart = String::from_utf8(sparkle_heart);

        assert!(sparkle_heart.is_err());
        assert_eq!(
            vec![0, 159, 146, 150],
            sparkle_heart.unwrap_err().into_bytes()
        );
    }
}
